﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using NetworkLib;
using System.Net.Sockets;
using System.IO;
using System.Timers;
using System.Security.Cryptography;
using System.Data;
using SettLib;
using DatabaseLib;

// WebServer Class v.1.0
// by George Makarov
// Email: georgemakarov1@yandex.ru


namespace WebServerLib
{
    public class cWebServer
    {
        List<string> DefaultFiles;
        List<stMimeTypes> MimeTypes;
        private cNetwork NetworkObj;
        public bool Running = false;
        List<cSession> Sessions;
        private Timer SessionTimer;
        private Timer ClearTempDirTimer;
        private DB db;

        private cSettings.cWebserverSett Settings;

        public cWebServer(cSettings.cWebserverSett pSett, DB pDB)
        {
            Sessions = new List<cSession>();
            MimeTypes = new List<stMimeTypes>();
            stMimeTypes MimeType = new stMimeTypes();
            MimeType.Extension = ".html";
            MimeType.MimeType = "text/html";
            MimeTypes.Add(MimeType);
            MimeType = new stMimeTypes();
            MimeType.Extension = ".htm";
            MimeType.MimeType = "text/html";
            MimeTypes.Add(MimeType);
            MimeType = new stMimeTypes();
            MimeType.Extension = ".bmp";
            MimeType.MimeType = "image/bmp";
            MimeTypes.Add(MimeType);
            MimeType = new stMimeTypes();
            MimeType.Extension = ".jpg";
            MimeType.MimeType = "image/jpeg";
            MimeTypes.Add(MimeType);
            MimeType = new stMimeTypes();
            MimeType.Extension = ".png";
            MimeType.MimeType = "image/png";
            MimeTypes.Add(MimeType);
            DefaultFiles = new List<string>();
            DefaultFiles.Add("default.html");
            DefaultFiles.Add("default.htm");
            DefaultFiles.Add("index.html");
            DefaultFiles.Add("index.htm");
            Settings = pSett;
            db = pDB;
            //Port = (int)aPort;
            //wwwPath = awwwPath;
            NetworkObj = new cNetwork();
            NetworkObj.TCPReceiveHandler += OnTCPReceive;
            //NetworkObj.TCPErrorHandler += OnTCPError;
            SessionTimer = new Timer(1000);
            SessionTimer.Elapsed += CheckSessionTimeout;
            ClearTempDirTimer = new Timer(Settings.TempDirClearPeriod*1000);
            ClearTempDirTimer.Elapsed += ClearTempDir;            

        }

        public delegate string dCommandHandler(string command, int SessionNum);
        public delegate void dErrorHandler(Exception ex);
        //public delegate void dExecuteQuery(string Query);
        //public delegate DataTable dGetQueryTable(string Query);
        public delegate void dLogHandler(string Msg);

        public event dCommandHandler CommandHandler;
        public event dErrorHandler ErrorHandler;
        //public event dExecuteQuery ExecuteQuery;
        //public event dGetQueryTable GetQueryTable;
        public event dLogHandler LogHandler;

        private void ClearTempDir(object sender, ElapsedEventArgs e)
        {
            string TempDirPath = Path.Combine(Settings.wwwPath, "tmp");
            if (!Directory.Exists(TempDirPath))
                return;
            DirectoryInfo di = new DirectoryInfo(TempDirPath);
            FileInfo[] TmpFiles = di.GetFiles("*.*");
            foreach (FileInfo TmpFile in TmpFiles)
            {
                if (TmpFile.CreationTime < (DateTime.Now.AddSeconds(-Settings.TempDirClearPeriod)))
                {
                    TmpFile.Delete();
                }
            }
        }

        public static List<cWebServer.stParam> FindParams(List<cWebServer.stParam> Params, string ParamName)
        {
            List<cWebServer.stParam> Result = new List<stParam>();
            if (Params != null)
            {
                foreach (stParam Param in Params)
                {
                    if (Param.ParamName.IndexOf(ParamName, StringComparison.CurrentCultureIgnoreCase) != -1)
                        Result.Add(Param);
                }
            }
            return Result;
        }

        public static object GetHeaderValue(List<cWebServer.stHeader> Headers, string HeaderName)
        {
            object Result = null;
            if (Headers != null)
            {
                foreach (cWebServer.stHeader Header in Headers)
                {
                    if (Header.HeaderName.ToLower() == HeaderName.ToLower())
                    {
                        Result = Header.Value;
                        break;
                    }
                }
            }
            return Result;
        }

        public static string GetMd5Hash(string input)
        {
            MD5CryptoServiceProvider md5Hasher = new MD5CryptoServiceProvider();

            byte[] data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(input));

            StringBuilder sBuilder = new StringBuilder();

            for (int i = 0; i < data.Length; i++)
            {
                sBuilder.Append(data[i].ToString("x2"));
            }

            return sBuilder.ToString();
        }

        public static List<cWebServer.stParam> GetParamsByValue(List<cWebServer.stParam> Params, string ParamValue)
        {
            List<cWebServer.stParam> Result = new List<stParam>();
            if (Params != null)
            {
                foreach (stParam Param in Params)
                {
                    if (Param.Value.IndexOf(ParamValue, StringComparison.CurrentCultureIgnoreCase) != -1)
                        Result.Add(Param);
                }
            }
            return Result;
        }

        public static object GetParamValue(List<cWebServer.stParam> Params, string ParamName)
        {
            object Result = null;
            if (Params != null)
            {
                foreach (cWebServer.stParam Param in Params)
                {
                    if (Param.ParamName.ToLower() == ParamName.ToLower())
                    {
                        Result = Param.Value;
                        break;
                    }
                }
            }
            return Result;
        }

        public cSession GetSessionData(int SessionNum)
        {
            return Sessions[SessionNum];
        }

        public string GetWWWPath()
        {
            return Settings.wwwPath;
        }

        public static string ParamsToURI(List<cWebServer.stParam> Params)
        {
            string Result = string.Empty;
            if ((Params != null) && (Params.Count > 0))
            {
                foreach (stParam Param in Params)
                {
                    if (Result.Length > 0)
                        Result += "&";
                    Result += Param.ParamName + "=" + Param.Value;
                }
            }
            return Result;
        }

        public void Start()
        {
            if (NetworkObj.TCPWaitMessage(Settings.Port))
            {
                SessionTimer.Start();
                ClearTempDirTimer.Start();
                Running = true;
                if (LogHandler != null)
                    LogHandler("Webserver started");
            }
        }

        public void Stop()
        {
            ClearTempDirTimer.Stop();
            SessionTimer.Stop();
            NetworkObj.TCPStopWaitMessage();
            Running = false;
            if (LogHandler != null)
                LogHandler("Webserver stopped");
        }

        public static List<cWebServer.stParam> UpdateParams(List<cWebServer.stParam> Params, string ParamName, string Value)
        {
            List<cWebServer.stParam> Result = Params;
            if (Params != null)
            {
                bool updated = false;
                if (Result.Count > 0)
                {
                    for (int i = 0; i < Result.Count; i++)
                    {
                        stParam Param = Result[i];
                        if (Param.ParamName.ToLower() == ParamName.ToLower())
                        {
                            Param.Value = Value;
                            Result[i] = Param;
                            updated = true;
                            break;
                        }
                    }
                }
                if (!updated)
                {
                    stParam Param = new stParam();
                    Param.ParamName = ParamName;
                    Param.Value = Value;
                    Result.Add(Param);
                }
            }
            return Result;
        }

        public void LoginUser(string Login, string Password)
        {
            if (Sessions.Count != 0)
            {                
                Sessions[Sessions.Count - 1].Params = 
                UpdateParams(Sessions[Sessions.Count - 1].Params, "webcommand", "auth");
                Sessions[Sessions.Count - 1].Params =
                UpdateParams(Sessions[Sessions.Count - 1].Params, "login", Login);
                Sessions[Sessions.Count - 1].Params =
                UpdateParams(Sessions[Sessions.Count - 1].Params, "pass", Password);
                ExecuteWebCommands(Sessions.Count - 1);
            }
        }

        private void CheckSessionTimeout(object source, ElapsedEventArgs e)
        {
            if (Sessions.Count != 0)
            {
                for (int i = 0; i < Sessions.Count; i++)
                {
                    cSession Session = Sessions[i];
                    Session.TimeOut -= 1000;
                    if (Session.TimeOut > 0)
                        Sessions[i] = Session;
                    else
                    {
                        Sessions.RemoveAt(i);
                        i--;
                    }
                }
            }
        }

        private int CreateSessionInfo(Guid SessionID, IPAddress IP, List<stParam> Params, List<stHeader> Headers, List<stCookie> Cookies)
        {
            cSession Session = new cSession();
            Session.IP = IP;
            Session.SessionID = SessionID;
            Session.TimeOut = Settings.SessionTimeout*1000;
            Session.Params = Params;
            Session.Headers = Headers;
            Session.Cookies = Cookies;
            Sessions.Add(Session);
            return Sessions.Count - 1;
        }

        private void ExecuteWebCommands(int SessionNum)
        {
            List<stParam> Params = Sessions[SessionNum].Params;
            object oCommand = GetParamValue(Params, "webcommand");
            if (oCommand != null)
            {
                switch (oCommand.ToString().ToLower())
                {
                    case "auth":
                        {
                            object oName = GetParamValue(Params, "login");
                            string sName = string.Empty;
                            if (oName != null)
                                sName = oName.ToString();
                            object oPassword = GetParamValue(Params, "pass");
                            string sPassword = string.Empty;
                            if (oPassword != null)
                                sPassword = oPassword.ToString();

                            if (LogHandler != null)
                                LogHandler("Попытка авторизации пользователя: Логин: "+sName+", Пароль: "+sPassword+", IP: "+Sessions[SessionNum].IP.ToString());

                            DataTable DTUsers = db.GetQueryTable("Select TAL.Level, TU.UserID from TUsers TU left join TAccessLevels TAL on TU.AccessLevelID = TAL.AccessLevelID where TU.Name ='" + sName + "' and TU.Password ='" + GetMd5Hash(sPassword) + "'");
                            if (DTUsers.Rows.Count != 0)
                            {
                                cSession Session = Sessions[SessionNum];
                                Session.AccessLevel = (int)DTUsers.Rows[0]["Level"];
                                Session.Name = sName;
                                Session.UserID = (Guid)DTUsers.Rows[0]["UserID"];
                                Sessions[SessionNum] = Session;
                                if (LogHandler != null)
                                    LogHandler("Пользователь авторизован: "+sName);
                            }
                            else
                            if (LogHandler != null)
                                LogHandler("Неудачная попытка авторизации");
                            break;
                        }
                    case "logout":
                        {
                            cSession Session = Sessions[SessionNum];
                            string UName = Session.Name;
                            Session.Name = string.Empty;
                            Session.AccessLevel = -1;
                            Session.UserID = Guid.Empty;
                            Sessions[SessionNum] = Session;
                            if (LogHandler != null)
                                LogHandler("Выход пользователя из системы: "+UName);
                            break;
                        }
                }
            }
        }

        private object GetCookieValue(List<cWebServer.stCookie> Cookies, string CookieName)
        {
            object Result = null;
            if (Cookies != null)
            {
                foreach (cWebServer.stCookie Cookie in Cookies)
                {
                    if (Cookie.CookieName.ToLower() == CookieName.ToLower())
                    {
                        Result = Cookie.Value;
                        break;
                    }
                }
            }
            return Result;
        }

        public static List<cWebServer.stCookie> FindCookies(List<cWebServer.stCookie> Cookies, string CookieName)
        {
            List<cWebServer.stCookie> Result = new List<stCookie>();
            if (Cookies != null)
            {
                foreach (stCookie Cookie in Cookies)
                {
                    if (Cookie.CookieName.IndexOf(CookieName, StringComparison.CurrentCultureIgnoreCase) != -1)
                        Result.Add(Cookie);
                }
            }
            return Result;
        }

        private string GetDefaultFile()
        {
            string Result = string.Empty;
            foreach (string DefaultFile in DefaultFiles)
            {
                if (File.Exists(Path.Combine(Settings.wwwPath, DefaultFile)))
                {
                    Result = DefaultFile;
                    break;
                }
            }
            return Result;

        }

        private string GetMimeType(string sRequestedFile)
        {

            String sMimeType = string.Empty;
            String sFileExt = string.Empty;
            if (MimeTypes != null)
            {
                sRequestedFile = sRequestedFile.ToLower();
                foreach (stMimeTypes MimeType in MimeTypes)
                {
                    if (MimeType.Extension == Path.GetExtension(sRequestedFile))
                    {
                        sMimeType = MimeType.MimeType;
                        break;
                    }
                }
            }
            return sMimeType;
        }

        //private Guid GetSessionID(string Request)
        //{
        //    Guid Result = Guid.Empty;
        //    int NameIndex = Request.IndexOf("SessionID");
        //    if (NameIndex != -1)
        //    {
        //        Result = new Guid(Request.Substring(NameIndex + 10, 36));
        //    }
        //    return Result;

        //}

        private int GetSessionInfo(List<stCookie> Cookies)
        {
            int Result = -1;
            if (Cookies != null)
            {
                foreach (stCookie Cookie in Cookies)
                {
                    int SessionNum = GetSessionInfo(new Guid(Cookie.Value));
                    if (SessionNum != -1)
                        Result = SessionNum;
                }
            }
            return Result;
        }

        private int GetSessionInfo(Guid SessionID)
        {
            int Result = -1;
            if (Sessions.Count > 0)
            {
                for (int i = 0; i < Sessions.Count; i++)
                {
                    if (Sessions[i].SessionID.Equals(SessionID))
                    {
                        Result = i;
                        break;
                    }
                }
            }
            return Result;
        }

        private string IncludeTrailingBackslash(string Path)
        {
            if (Path[Path.Length - 1] == '\\')
                return Path;
            else
                return Path + '\\';
        }

        private byte[] InsertBytesToBytes(byte[] Source1, byte[] Source2, int InsertIndex, int NumBytesToRemove)
        {
            byte[] Result = new byte[Source1.Length + Source2.Length - NumBytesToRemove];
            Array.Copy(Source1, 0, Result, 0, InsertIndex);
            Array.Copy(Source2, 0, Result, InsertIndex, Source2.Length);
            Array.Copy(Source1, InsertIndex + NumBytesToRemove, Result, InsertIndex + Source2.Length, Source1.Length - InsertIndex - NumBytesToRemove);
            return Result;
        }

        private byte[] InsertStringToBytes(byte[] Source1, string Source2, int InsertIndex, int NumBytesToRemove)
        {
            return InsertBytesToBytes(Source1, UTF8Encoding.UTF8.GetBytes(Source2), InsertIndex, NumBytesToRemove);
        }

        private void OnTCPReceive(cNetwork.stTCPReceiveData ReceiveData)
        {
            //if (ReceiveData.Data.Substring(0, 3) != "GET")
            //{
            //    ReceiveData.client.Close();
            //    return;
            //}

            string StatusCode = " 200 OK";
            string sRequestedFile = string.Empty;
            string RootPath = Settings.wwwPath.TrimEnd('\\');
            int iStartPos = 0;
            string sHttpVersion = string.Empty;
            string sRequest = string.Empty;
            bool ErrorOccurred = false;
            string sHeaders = string.Empty;
            List<stHeader> Headers = null;
            List<stCookie> Cookies = null;
            List<stParam> Params = null;
            int iSession = 0;

            if (ReceiveData.Data.Length == 0)
            {
                if (ErrorHandler != null)
                    ErrorHandler(new Exception("Пустой запрос"));
                StatusCode = " 400 Bad Request";
                sRequestedFile = "400.htm";
                ErrorOccurred = true;
            }

            if (!ErrorOccurred)
            {
                try
                {
                    iStartPos = ReceiveData.Data.IndexOf("HTTP", 1);
                    sHttpVersion = ReceiveData.Data.Substring(iStartPos, 8);
                    sRequest = ReceiveData.Data.Substring(0, iStartPos - 1);
                    sRequest = sRequest.Replace("\\", "/");
                    if ((sRequest.IndexOf(".") < 1) && (!sRequest.EndsWith("/")))
                        sRequest = sRequest + "/";
                }
                catch (Exception ex)
                {
                    if (ErrorHandler != null)
                        ErrorHandler(new Exception("Ошибка при разборе запроса\nЗапрос: " + ReceiveData.Data, ex));
                    StatusCode = " 400 Bad Request";
                    sRequestedFile = "400.htm";
                    ErrorOccurred = true;
                }
            }


            if (!ErrorOccurred)
            {
                try
                {
                    //Get headers data
                    sHeaders = ReceiveData.Data.Substring(iStartPos + 8).Trim();
                    Headers = new List<stHeader>();
                    foreach (string sHeader in sHeaders.Split('\n'))
                    {
                        string[] HeaderDetails = sHeader.Split(':');
                        stHeader Header = new stHeader();
                        Header.HeaderName = HeaderDetails[0].Trim();
                        Header.Value = HeaderDetails[1].Trim();
                        Headers.Add(Header);
                    }
                }
                catch (Exception ex)
                {
                    if (ErrorHandler != null)
                        ErrorHandler(new Exception("Ошибка при разборе заголовков\nЗапрос: " + ReceiveData.Data, ex));
                    StatusCode = " 400 Bad Request";
                    sRequestedFile = "400.htm";
                    ErrorOccurred = true;
                }
            }

            if (!ErrorOccurred)
            {
                try
                {
                    //Get cookies data
                    Cookies = new List<stCookie>();

                    object oCookie = GetHeaderValue(Headers, "Cookie");
                    if (oCookie != null)
                    {
                        string ssCookie = oCookie.ToString().Trim();
                        if (ssCookie.Length > 0)
                        {
                            foreach (string sfCookie in ssCookie.Split(';'))
                            {
                                string[] CookieDetails = sfCookie.Split('=');
                                stCookie Cookie = new stCookie();
                                Cookie.CookieName = CookieDetails[0].Trim();
                                Cookie.Value = CookieDetails[1].Trim();
                                Cookies.Add(Cookie);
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    if (ErrorHandler != null)
                        ErrorHandler(new Exception("Ошибка при разборе куки\nЗапрос: " + ReceiveData.Data, ex));
                    StatusCode = " 400 Bad Request";
                    sRequestedFile = "400.htm";
                    ErrorOccurred = true;
                }
            }

            if (!ErrorOccurred)
            {
                try
                {
                    //Get params data
                    Params = new List<stParam>();
                    iStartPos = sRequest.IndexOf('?');
                    if (iStartPos != -1)
                    {
                        foreach (string ParamData in sRequest.Substring(iStartPos + 1).Split('&'))
                        {
                            string[] ParamDetails = ParamData.Split('=');
                            stParam Param = new stParam();
                            Param.ParamName = ParamDetails[0];
                            Param.Value = Uri.UnescapeDataString(ParamDetails[1]);
                            Params.Add(Param);
                        }
                        sRequest = sRequest.Substring(0, iStartPos);
                    }
                }
                catch (Exception ex)
                {
                    if (ErrorHandler != null)
                        ErrorHandler(new Exception("Ошибка при разборе параметров из URI\nЗапрос: " + ReceiveData.Data, ex));
                    StatusCode = " 400 Bad Request";
                    sRequestedFile = "400.htm";
                    ErrorOccurred = true;
                }
            }

            if (!ErrorOccurred)
            {
                try
                {
                    //Get SessionID from coockies
                    List<stCookie> SessionIDs = FindCookies(Cookies, "SessionID");
                    //object oSessionID = GetCookieValue(Cookies, "SessionID");
                    //Guid SessionID = Guid.Empty;
                    //if (oSessionID != null)
                    //    SessionID = new Guid(oSessionID.ToString());


                    //update session info
                    iSession = GetSessionInfo(SessionIDs);
                    if ((SessionIDs.Count == 0) || (iSession == -1))
                    {
                        Guid SessionID = Guid.NewGuid();
                        Cookies = UpdateCookies(Cookies, "SessionID", SessionID.ToString());
                        iSession = CreateSessionInfo(SessionID, ReceiveData.IP, Params, Headers, Cookies);
                    }
                    else
                        UpdateSessionInfo(iSession, Params, Headers, Cookies);
                }
                catch (Exception ex)
                {
                    if (ErrorHandler != null)
                        ErrorHandler(new Exception("Ошибка при работе с сессией\nЗапрос: " + ReceiveData.Data, ex));
                    StatusCode = " 500 Internal Server Error";
                    sRequestedFile = "500.htm";
                    ErrorOccurred = true;
                }
            }

            if (!ErrorOccurred)
            {
                iStartPos = sRequest.LastIndexOf("/") + 1;
                sRequestedFile = sRequest.Substring(iStartPos);
            }

            string LocalPath;
            if (!ErrorOccurred)
            {
                string sDirName = sRequest.Substring(sRequest.IndexOf("/"), sRequest.LastIndexOf("/") - 3);
                LocalPath = RootPath + sDirName.Replace('/', '\\');
            }
            else
            {
                LocalPath = RootPath;
            }

            if (sRequestedFile == string.Empty)
            {
                sRequestedFile = GetDefaultFile();
                if (sRequestedFile == string.Empty)
                {
                    StatusCode = " 404 Not Found";
                    sRequestedFile = "404.htm";
                    LocalPath = RootPath;
                }
            }

            string sMimeType = GetMimeType(sRequestedFile);
            string sLocalFullPath = Path.Combine(LocalPath, sRequestedFile);

            if (!File.Exists(sLocalFullPath))
            {
                StatusCode = " 404 Not Found";
                sRequestedFile = "404.htm";
                sMimeType = GetMimeType(sRequestedFile);
                LocalPath = RootPath;
                sLocalFullPath = Path.Combine(LocalPath, sRequestedFile);
            }

            byte[] bytes = null;
            try
            {
                bytes = ReadFile(sLocalFullPath);
            }
            catch (Exception ex)
            {
                if (ErrorHandler != null)
                    ErrorHandler(new Exception("Ошибка при чтении файла\nЗапрос: " + ReceiveData.Data, ex));
                StatusCode = " 500 Internal Server Error";
                sRequestedFile = "500.htm";
                sMimeType = GetMimeType(sRequestedFile);
                bytes = ReadFile(Path.Combine(RootPath, sRequestedFile));
            }

            if (!ErrorOccurred)
            {
                try
                {                    
                    ExecuteWebCommands(iSession);
                }
                catch (Exception ex)
                {
                    if (ErrorHandler != null)
                        ErrorHandler(new Exception("Ошибка при работе выполнении вебкоманды\nЗапрос: " + ReceiveData.Data, ex));
                    StatusCode = " 500 Internal Server Error";
                    sRequestedFile = "500.htm";
                    sMimeType = GetMimeType(sRequestedFile);
                    bytes = ReadFile(Path.Combine(RootPath, sRequestedFile));
                    ErrorOccurred = true;
                }
            }

            if (!ErrorOccurred)
            {
                try
                {
                    object CommandValue = GetParamValue(Params, "command");
                    if (CommandValue != null)
                        CommandHandler(CommandValue.ToString(), iSession);
                }
                catch (Exception ex)
                {
                    if (ErrorHandler != null)
                        ErrorHandler(new Exception("Ошибка при работе выполнении команды из URI\nЗапрос: " + ReceiveData.Data, ex));
                    StatusCode = " 500 Internal Server Error";
                    sRequestedFile = "500.htm";
                    sMimeType = GetMimeType(sRequestedFile);
                    bytes = ReadFile(Path.Combine(RootPath, sRequestedFile));
                    ErrorOccurred = true;
                }
            }

            if (!ErrorOccurred)
            {
                try
                {
                    string WebPage;
                    bool IncludePresent = false;
                    bool CommandPresent = false;
                    do
                    {
                        WebPage = ASCIIEncoding.ASCII.GetString(bytes);
                        IncludePresent = WebPage.IndexOf("%include(") != -1;
                        CommandPresent = WebPage.IndexOf("%command(") != -1;
                        do
                        {
                            WebPage = ASCIIEncoding.ASCII.GetString(bytes);
                            iStartPos = WebPage.IndexOf("%include(");
                            if (iStartPos == -1)
                                continue;
                            int iEndPos = WebPage.IndexOf(")include%");
                            string IncludingPage = Path.Combine(LocalPath, WebPage.Substring(iStartPos + 9, iEndPos - iStartPos - 9));
                            byte[] bIncPage;
                            if (File.Exists(IncludingPage))
                            {
                                bIncPage = ReadFile(IncludingPage);
                                bytes = InsertBytesToBytes(bytes, bIncPage, iStartPos, iEndPos - iStartPos + 9);
                            }
                            else
                            {
                                bytes = InsertStringToBytes(bytes, "Ошибка 404. Включаемая страница не найдена", iStartPos, iEndPos - iStartPos + 9);

                            }
                            WebPage = WebPage.Remove(iStartPos, iEndPos - iStartPos + 9);
                        } while (iStartPos != -1);

                        if (CommandHandler != null)
                        {
                            do
                            {
                                WebPage = ASCIIEncoding.ASCII.GetString(bytes);
                                iStartPos = WebPage.IndexOf("%command(");
                                if (iStartPos == -1)
                                    continue;
                                int iEndPos = WebPage.IndexOf(")command%");
                                string commandResult = CommandHandler(WebPage.Substring(iStartPos + 9, iEndPos - iStartPos - 9), iSession);
                                bytes = InsertStringToBytes(bytes, commandResult, iStartPos, iEndPos - iStartPos + 9);
                                WebPage = WebPage.Remove(iStartPos, iEndPos - iStartPos + 9);

                            } while (iStartPos != -1);
                        }
                    }
                    while (IncludePresent || CommandPresent);
                }
                catch (Exception ex)
                {
                    if (ErrorHandler != null)
                        ErrorHandler(new Exception("Ошибка при работе выполнении команды или при вложении файла\nЗапрос: " + ReceiveData.Data, ex));
                    StatusCode = " 500 Internal Server Error";
                    sRequestedFile = "500.htm";
                    sMimeType = GetMimeType(sRequestedFile);
                    bytes = ReadFile(Path.Combine(RootPath, sRequestedFile));
                }
            }

            try
            {
                SendHeader(sHttpVersion, sMimeType, bytes.Length, StatusCode, Cookies, ref ReceiveData.client);
            }
            catch (Exception ex)
            {
                if (ErrorHandler != null)
                    ErrorHandler(new Exception("Ошибка при отправке заголовка клиенту\nЗапрос: " + ReceiveData.Data, ex));
            }

            try
            {
                NetworkObj.TCPSendMessage(bytes, ref ReceiveData.client);
                ReceiveData.client.Close();
            }
            catch (Exception ex)
            {
                if (ErrorHandler != null)
                    ErrorHandler(new Exception("Ошибка при отправке данных клиенту\nЗапрос: " + ReceiveData.Data, ex));
            }
        }

        private byte[] ReadFile(string Filename)
        {
            FileStream fs = new FileStream(Filename, FileMode.Open, FileAccess.Read, FileShare.Read);
            BinaryReader br = new BinaryReader(fs);
            byte[] Result = new byte[fs.Length];
            br.Read(Result, 0, Result.Length);
            br.Close();
            return Result;
        }

        private void SendHeader(string sHttpVersion, string sMIMEHeader, int iTotBytes, string sStatusCode, List<stCookie> Cookies, ref Socket socket)
        {
            String sBuffer = "";
            // if Mime type is not provided set default to text/html
            if (sMIMEHeader.Length == 0)
            {
                sMIMEHeader = "text/html";
            }
            sBuffer = sBuffer + sHttpVersion + sStatusCode + "\r\n";
            sBuffer = sBuffer + "Server: dlink's syslog messages analyser web server (c) KARS\r\n";
            sBuffer = sBuffer + "Content-Type: " + sMIMEHeader + "\r\n";
            if ((Cookies != null) && (Cookies.Count > 0))
                foreach (stCookie Cookie in Cookies)
                {
                    sBuffer += "Set-cookie: " + Cookie.CookieName + "=" + Cookie.Value + "\r\n";
                }
            sBuffer = sBuffer + "Accept-Ranges: bytes\r\n";
            sBuffer = sBuffer + "Content-Length: " + iTotBytes + "\r\n\r\n";
            Byte[] bSendData = Encoding.ASCII.GetBytes(sBuffer);
            NetworkObj.TCPSendMessage(bSendData, ref socket);

        }

        private List<cWebServer.stCookie> UpdateCookies(List<cWebServer.stCookie> Cookies, string CookieName, string Value)
        {
            List<cWebServer.stCookie> Result = Cookies;
            bool updated = false;
            if (Result.Count > 0)
            {
                for (int i = 0; i < Result.Count; i++)
                {
                    stCookie Cookie = Result[i];
                    if (Cookie.CookieName.ToLower() == CookieName.ToLower())
                    {
                        Cookie.Value = Value;
                        Result[i] = Cookie;
                        updated = true;
                        break;
                    }
                }
            }
            if (!updated)
            {
                stCookie Cookie = new stCookie();
                Cookie.CookieName = CookieName;
                Cookie.Value = Value;
                Result.Add(Cookie);
            }
            return Result;

        }

        private void UpdateSessionInfo(int iSession, List<stParam> Params, List<stHeader> Headers, List<stCookie> Cookies)
        {
            cSession Session = Sessions[iSession];
            Session.TimeOut = Settings.SessionTimeout*1000;
            Session.Params = Params;
            Session.Headers = Headers;
            Session.Cookies = Cookies;
            Sessions[iSession] = Session;
        }

        private class stMimeTypes
        {
		// Fields (2) 

            public string Extension;
            public string MimeType;
        }
        public class cSession
        {
            public int AccessLevel;
            private List<stCookie> _Cookies;
            public List<stCookie> Cookies
            {
                get
                {
                    return _Cookies;
                }
                set
                {
                    _Cookies = value;
                }
            }
            private List<stHeader> _Headers;
            public List<stHeader> Headers
            {
                get
                {
                    return _Headers;
                }
                set
                {
                    _Headers = value;
                }
            }
            public IPAddress IP;
            public string Name;
            private List<stParam> _Params;
            public List<stParam> Params
            {
                get
                {
                    return _Params;
                }
                set
                {
                    _Params = value;
                }
            }
            public Guid SessionID;
            public double TimeOut;
            public Guid UserID;

            public cSession()
            {
                //_Cookies = new List<stCookie>();
                //_Headers = new List<stHeader>();
                //_Params = new List<stParam>();
            }
        }
        public struct stParam
        {
		// Data Members (2) 

            public string ParamName;
            public string Value;
        }
        public struct stHeader
        {
		// Data Members (2) 

            public string HeaderName;
            public string Value;
        }
        public struct stCookie
        {

            public string CookieName;
            public string Value;
        }
    }
}
